package com.lancoo.cvas.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lancoo.cvas.entity.*;
import com.lancoo.cvas.exception.BizException;
import com.lancoo.cvas.mapper.*;
import com.lancoo.cvas.pojo.common.Deleted;
import com.lancoo.cvas.pojo.common.IsEnable;
import com.lancoo.cvas.pojo.common.TeachingClassType;
import com.lancoo.cvas.pojo.constant.GlobalConstant;
import com.lancoo.cvas.pojo.vo.*;
import com.lancoo.cvas.service.*;
import com.lancoo.cvas.util.RedisUtil;
import com.lancoo.cvas.util.TimeUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.nio.cs.FastCharsetProvider;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;


@Service("cssTaskInfoService")
public class CssTaskInfoServiceImpl extends ServiceImpl<CssTaskInfoMapper, CssTaskInfo> implements CssTaskInfoService {


    @Resource
    private CssTaskRoundService cssTaskRoundService;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private TaskInfoMapper taskInfoMapper;
    @Resource
    private StudentRetakeService studentRetakeService;

    @Resource
    private YunService yunService;


    @Resource
    private HttpServletRequest request;

    @Resource
    private CssTaskRoundMapper cssTaskRoundMapper;

    @Resource
    private TeachingClassMapper teachingClassMapper;

    @Resource
    private CourseMapper courseMapper;

    @Resource
    private CssRangeService cssRangeService;

    @Resource
    private CssRangeMapper cssRangeMapper;

    @Resource
    private TaskToGradeService taskToGradeService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addCssTask(TaskInfo taskInfo) {
        CssTaskInfo cssTaskInfoPri = new CssTaskInfo();
        cssTaskInfoPri.setTaskId(taskInfo.getTaskId())
                .setIsRetake(0)
                .setCssTaskName(taskInfo.getTermName() + "选课计划")
                .setCssTaskStatus(0);
        this.save(cssTaskInfoPri);
    }

    @Override
    public void removeCssInfo(List<Integer> taskIds) {
        List<Integer> cssTaskIds = this.lambdaQuery().in(CssTaskInfo::getTaskId, taskIds).list().stream()
                .map(CssTaskInfo::getCssTaskId).collect(Collectors.toList());

        if (CollUtil.isNotEmpty(cssTaskIds)) {
            cssTaskRoundService.lambdaUpdate().in(CssTaskRound::getCssTaskId, cssTaskIds).remove();
        }

        this.lambdaUpdate().in(CssTaskInfo::getTaskId, taskIds).remove();

    }

    /**
     * 获取选课任务
     *
     * @param taskId
     * @return
     */
    @Override
    public CssTaskInfo getCssTaskInfo(Integer taskId) {

        //获取选课任务
        CssTaskInfo cssTaskInfo = this.getOne(new LambdaQueryWrapper<CssTaskInfo>()
                .eq(CssTaskInfo::getTaskId, taskId)
                .eq(CssTaskInfo::getDeleted, Deleted.NOT.getCode()));
        if (ObjUtil.isNull(cssTaskInfo)) {
            throw new BizException("该计划未设置选课轮次");
        }
        return cssTaskInfo;
    }

    /**
     * 获取最早轮次开始时间小于当前时间返回false
     *
     * @param cssTaskId
     * @return
     */
    @Override
    public Boolean getEarliestRound(Integer cssTaskId) {
        //todo
        CssTaskRound cssTaskRound = cssTaskRoundService.lambdaQuery()
                .eq(CssTaskRound::getCssTaskId, cssTaskId)
                .eq(CssTaskRound::getDeleted, Deleted.NOT.getCode())
                .orderByAsc(CssTaskRound::getBeginTime)
                .last("limit 1").one();
        if (ObjUtil.isNotNull(cssTaskRound)) {
            return cssTaskRound.getBeginTime().isAfter(LocalDateTime.now());
        }
        return true;
    }


    /**
     * 批量删除轮次
     *
     * @param cssTaskId
     * @param selectRound
     * @return
     */
    @Override
    public Boolean removeUselessRound(Integer cssTaskId, List<Integer> selectRound) {
        if (CollUtil.isEmpty(selectRound)) {
            return true;
        }

        //获取对应轮次
        List<CssTaskRound> cssTaskRounds = cssTaskRoundService.lambdaQuery().eq(CssTaskRound::getCssTaskId, cssTaskId).in(CssTaskRound::getSelectRound, selectRound).list();
        List<Integer> roundIds = cssTaskRounds.stream().map(CssTaskRound::getId).collect(Collectors.toList());

        //如果轮次不为空，修改相应信息，同时清除对应缓存
        if (CollUtil.isNotEmpty(roundIds)) {
            cssTaskRoundService.lambdaUpdate().in(CssTaskRound::getId, roundIds).set(CssTaskRound::getDeleted, Deleted.YES.getCode()).update();
            cssRangeService.lambdaUpdate().in(CssRange::getRoundId, roundIds).set(CssRange::getDeleted, Deleted.YES.getCode()).update();
        }
        redisUtil.del(GlobalConstant.REDIS_ROUND_KEY);
        return true;
    }

    /**
     * 同步重修学生
     *
     * @param cssTaskInfo
     * @return
     */
    @Override
    public CssTaskInfo updateCssTaskInfo(CssTaskInfo cssTaskInfo) {
        this.updateById(cssTaskInfo);
        //设置选课轮次时同步重修学生
        TaskInfo taskInfo = taskInfoMapper.selectById(cssTaskInfo.getTaskId());
        if (ObjUtil.isNotNull(taskInfo)) {
            List<StudentRetake> studentRetakes = studentRetakeService.lambdaQuery().eq(StudentRetake::getTaskId, cssTaskInfo.getTaskId()).list();
            if (CollUtil.isEmpty(studentRetakes)) {
                //同步重修学生
                yunService.syncRetakeStudent(cssTaskInfo.getTaskId(), taskInfo.getSchoolId(), taskInfo.getTerm());
            }
        }
        //清除redis缓存
        redisUtil.del(GlobalConstant.REDIS_ROUND_KEY);
        return cssTaskInfo;
    }

    @Override
    public List<CssTaskRound> getCssTaskRound(Integer cssTaskId, Integer selectRound) {
        //获取对应选课轮次，并排序
        List<CssTaskRound> cssTaskRounds = cssTaskRoundService.lambdaQuery()
                .eq(CssTaskRound::getCssTaskId, cssTaskId)
                .eq(CssTaskRound::getSelectRound, selectRound)
                .eq(CssTaskRound::getDeleted, Deleted.NOT.getCode())
                .list().stream()
                .sorted(Comparator.comparing(CssTaskRound::getStage)).collect(Collectors.toList());
        return cssTaskRounds;
    }


    /**
     * 更新或添加选课轮次
     *
     * @param cssTaskRound
     * @return
     */
    @Override
    public CssTaskRound addOrUpdateCssTaskRound(CssTaskRound cssTaskRound) {
        //修改对应选课信息
        cssTaskRoundService.saveOrUpdate(cssTaskRound);
        //获取选课任务
        CssTaskInfo cssTaskInfo = this.getById(cssTaskRound.getCssTaskId());

        //发送消息
        String userId = request.getHeader("userId");
        String userName = request.getHeader("userName");
        String authorization = request.getHeader("Authorization");


        String module = "";
        if (cssTaskRound.getSelectRound() == 1) {
            module = "正选";
        } else if (cssTaskRound.getSelectRound() == 2) {
            module = "补选";
        } else if (cssTaskRound.getSelectRound() == 3) {
            module = "退选";
        }

        //选课发送通知
        if (StrUtil.isNotBlank(module) && ObjUtil.isNotNull(cssTaskRound.getBeginTime()) && ObjUtil.isNotNull(cssTaskRound.getEndTime())) {
            yunService.sendSelectCourseStartMsg(cssTaskRound.getBeginTime(), cssTaskRound.getEndTime(), cssTaskInfo.getTaskId(), module, userId, userName, authorization);
        }
        //清除缓存
        redisUtil.del(GlobalConstant.REDIS_ROUND_KEY);

        return cssTaskRound;
    }

    @Override
    public List<CssRangeDTO> getRange(Integer taskId, Long cssTaskId, Integer flag) {

        //获取对应课程id 以及课程名称
        List<CssRangeDTO> cssRangeDTOList = new ArrayList<>();

        List<CssTaskRound> cssTaskRoundList = cssTaskRoundMapper.selectList(new LambdaQueryWrapper<CssTaskRound>()
                .eq(CssTaskRound::getCssTaskId, cssTaskId)
                .eq(CssTaskRound::getSelectRound, flag)
                .eq(CssTaskRound::getDeleted, Deleted.NOT.getCode()));
        List<TeachingClass> teachingClassList = teachingClassMapper.selectList(new LambdaQueryWrapper<TeachingClass>().eq(TeachingClass::getTaskId, taskId)
                .eq(TeachingClass::getTeachingClassType, TeachingClassType.SELECT.getCode())
                .eq(TeachingClass::getDeleted, Deleted.NOT.getCode()));

        List<Integer> roundIds = cssTaskRoundList.stream().map(CssTaskRound::getId).distinct().collect(Collectors.toList());

        List<CssRange> cssRangeList = new ArrayList<>();
        if (CollUtil.isNotEmpty(roundIds)) {
            cssRangeList = cssRangeMapper.selectList(new LambdaQueryWrapper<CssRange>().in(CssRange::getRoundId, roundIds)
                    .eq(CssRange::getDeleted, Deleted.NOT.getCode()));

        }


        //
        List<Course> courseList = courseMapper.selectList(new LambdaQueryWrapper<Course>()
                .eq(Course::getTaskId, taskId).eq(Course::getDeleted, Deleted.NOT.getCode()));
        for (CssTaskRound cssTaskRound : cssTaskRoundList) {
            CssRangeDTO cssRangeDTO = new CssRangeDTO();
            cssRangeDTO.setCssTaskId(cssTaskRound.getCssTaskId());
            cssRangeDTO.setSelectRound(cssTaskRound.getSelectRound());
            cssRangeDTO.setStage(cssTaskRound.getStage());


            Map<String, String> gradeMap = cssRangeList.stream().filter(p -> p.getRoundId().equals(cssTaskRound.getId()))
                    .collect(Collectors.toList()).stream().collect(Collectors.toMap(CssRange::getGradeId, CssRange::getGradeName));

            List<GradeDto> gradeDtos = new ArrayList<>();
            gradeMap.forEach((key, value) -> {
                GradeDto gradeDto = new GradeDto();
                gradeDto.setGradeId(key);
                gradeDto.setGradeName(value);
                gradeDtos.add(gradeDto);

            });

            cssRangeDTO.setGradeIds(gradeDtos);


            List<TeachingClass> teachingClasses = teachingClassList.stream().filter(p -> {
                for (String gradeId : gradeMap.keySet()) {
                    if (p.getGradeId().equals(gradeId)) {
                        return true;
                    }
                }
                return false;
            }).collect(Collectors.toList());


            List<Long> courseIds = teachingClasses.stream().map(TeachingClass::getCourseId).distinct().collect(Collectors.toList());

            List<Course> courses = courseList.stream().filter(p -> {
                for (Long courseId : courseIds) {
                    if (p.getCourseId().equals(courseId)) {
                        return true;
                    }
                }
                return false;
            }).collect(Collectors.toList());


            List<String> courseTypeId = courses.stream().map(Course::getCourseId).map(String::valueOf).distinct().collect(Collectors.toList());
            List<String> courseTypeName = courses.stream().map(Course::getCourseName).distinct().collect(Collectors.toList());

            cssRangeDTO.setCourseTypeId(courseTypeId);
            cssRangeDTO.setCourseTypeName(courseTypeName);

            cssRangeDTOList.add(cssRangeDTO);
        }
        return cssRangeDTOList;
    }


    /**
     * 删除选课轮次
     *
     * @param roundId
     * @return
     */
    @Override
    public Boolean removeCssTaskRound(Integer roundId) {
        cssTaskRoundService.lambdaUpdate().eq(CssTaskRound::getId, roundId).eq(CssTaskRound::getDeleted, Deleted.NOT.getCode())
                .set(CssTaskRound::getDeleted, Deleted.YES.getCode()).update();
        cssRangeService.lambdaUpdate().eq(CssRange::getRoundId, roundId).eq(CssRange::getDeleted, Deleted.NOT.getCode())
                .set(CssRange::getDeleted, Deleted.YES.getCode());
        redisUtil.del(GlobalConstant.REDIS_ROUND_KEY);
        return true;
    }

    /**
     * 获取选课任务
     *
     * @param getCssTask
     * @return
     */
    @Override
    public List<CssTaskRoundInfo> getCssTaskBySchool(GetCssTask getCssTask) {
        List<CssTaskRoundInfo> allRound = getRound();

        if(StringUtils.isNotBlank(getCssTask.getKeyWord())){
            Pattern pattern = Pattern.compile(".*"+getCssTask.getKeyWord()+".*");
            allRound =  allRound.stream().filter(p->pattern.matcher(p.getCssTaskName()).matches()).collect(Collectors.toList());
        }
        List<CssTaskRoundInfo> returnRound = new ArrayList<>();
        Iterator<CssTaskRoundInfo> iterator = allRound.iterator();
        while (iterator.hasNext()) {
            CssTaskRoundInfo cssTaskRoundInfo = iterator.next();
            //根据前端筛选条件过滤
            if (StrUtil.isNotBlank(getCssTask.getSchoolId()) && !cssTaskRoundInfo.getSchoolId().equals(getCssTask.getSchoolId())) {
                iterator.remove();
                continue;
            }

            if (StrUtil.isNotBlank(getCssTask.getKeyWord()) && !cssTaskRoundInfo.getCssTaskName().contains(getCssTask.getKeyWord())) {
                iterator.remove();
                continue;
            }

            //任务所属年级过滤
            if (!cssTaskRoundInfo.getTaskToGradeIds().contains(getCssTask.getGradeId())) {
                iterator.remove();
                continue;
            }

            //根据学院、年级范围过滤。预选、退选不需要过滤，所有学生都要显示。
            if (!cssTaskRoundInfo.getSelectRound().equals(3)) {
                if (cssTaskRoundInfo.getCssRanges().stream().noneMatch(cssRange -> (cssRange.getGradeId().equals(getCssTask.getGradeId())))) {
                    iterator.remove();
                }
            }
        }


        //todo 不需要处理
        //全部选课轮次，根据选课任务id分组。
        Map<Integer, List<CssTaskRoundInfo>> roundMap = allRound.stream().collect(Collectors.groupingBy(CssTaskRoundInfo::getCssTaskId));
        roundMap.forEach((k, v) -> {
           /*
                根据 轮次阶段倒序排序。然后根据时间计算状态
                1.进行中  直接保留
                2.已结束  直接保留
                3.待开始->进行中 保留后者
                4.待开始->已结束 保留前者
                5.待开始->待开始 保留后者
                 */
            v.sort(Comparator.comparing(CssTaskRoundInfo::getSelectRound).thenComparing(CssTaskRoundInfo::getStage).reversed());
            CssTaskRoundInfo firstCssTaskRound = v.get(0);
            int status = TimeUtils.getStatus(firstCssTaskRound.getBeginTime(), firstCssTaskRound.getEndTime());
            for (int i = 1; i < v.size(); i++) {
                CssTaskRoundInfo currentCssTaskRound = v.get(i);
                int currentStatus = TimeUtils.getStatus(currentCssTaskRound.getBeginTime(), currentCssTaskRound.getEndTime());
                if (status == 1 || status == 2 || (status == 3 && currentStatus == 1)) {
                    break;
                }
                if (status == 3 && currentStatus == 3) {
                    firstCssTaskRound = currentCssTaskRound;
                    continue;
                }
                if (status == 3 && currentStatus == 2) {
                    status = currentStatus;
                    firstCssTaskRound = currentCssTaskRound;
                    break;
                }
            }
            firstCssTaskRound.setState(status);
            returnRound.add(firstCssTaskRound);
        });
        return returnRound;
    }

    private List<CssTaskRoundInfo> getRound() {
        if (redisUtil.hasKey(GlobalConstant.REDIS_ROUND_KEY)) {
            return (List<CssTaskRoundInfo>) redisUtil.get(GlobalConstant.REDIS_ROUND_KEY);
        } else {

            List<CssTaskRoundInfo> allRoundInfo = cssRangeMapper.getAllRoundInfo();
            //年级
            Map<Integer, List<TaskToGrade>> gradeMap = taskToGradeService.lambdaQuery().eq(TaskToGrade::getDeleted, Deleted.NOT.getCode()).list()
                    .stream().collect(Collectors.groupingBy(TaskToGrade::getTaskId));
            //获取范围
            Map<Integer, List<CssRange>> rangeMap = cssRangeService.lambdaQuery().eq(CssRange::getDeleted, Deleted.NOT.getCode()).list().stream()
                    .collect(Collectors.groupingBy(CssRange::getRoundId));
            allRoundInfo.forEach(a -> {
                List<CssRange> cssRanges = rangeMap.get(a.getId());
                List<TaskToGrade> taskToGrades = gradeMap.get(a.getTaskId());
                if (CollUtil.isNotEmpty(cssRanges)) {
                    a.setCssRanges(cssRanges);
                }
                if (CollUtil.isNotEmpty(taskToGrades)) {
                    a.setTaskToGradeIds(taskToGrades.stream().map(TaskToGrade::getGradeId).collect(Collectors.toList()));
                }
            });
            redisUtil.set(GlobalConstant.REDIS_ROUND_KEY, allRoundInfo, 60 * 60 * 24 * 7);
            return allRoundInfo;
        }
    }

    @Transactional
    @Override
    public List<CssRange> setRange(List<CssRange> cssRanges, Integer roundId) {

        /**
         * 删除之前的设置
         */
        cssRangeMapper.update(null, new LambdaUpdateWrapper<CssRange>().eq(CssRange::getRoundId, roundId)
                .set(CssRange::getDeleted, Deleted.YES.getCode()));

        cssRangeService.saveBatch(cssRanges);
        redisUtil.del(GlobalConstant.REDIS_ROUND_KEY);
        return cssRanges;
    }


    /**
     * 获取所有选课课程
     *
     * @param taskId
     * @param roundId
     * @return
     */
    @Override
    public List<Course> getCourseList(Integer taskId) {


        List<Course> courseList = new ArrayList<>();
//        List<TeachingClass> teachingClassList = teachingClassMapper.selectList(new LambdaQueryWrapper<TeachingClass>()
//                .eq(TeachingClass::getTaskId, taskId)
//                .eq(TeachingClass::getTeachingClassType, TeachingClassType.SELECT.getCode())
//                .eq(TeachingClass::getIsEnable, IsEnable.ENABLE.getCode())
//                .or().eq(TeachingClass::getIsEnable,6)
//                .eq(TeachingClass::getDeleted, Deleted.NOT.getCode()));

        List<Long> courseIds = teachingClassMapper.getSelectionTeachingClassByWeek(taskId);

//        List<Long> courseIds = teachingClassList.stream().map(TeachingClass::getCourseId).distinct().collect(Collectors.toList());

        if (CollUtil.isNotEmpty(courseIds)) {

            courseList = courseMapper.selectList(new LambdaQueryWrapper<Course>().in(Course::getCourseId, courseIds).eq(Course::getDeleted, Deleted.NOT.getCode()));
        }
        return courseList;
    }

    @Override
    public CourseRange getCourseRange(Integer taskId, List<String> gradeIds) {
        CourseRange courseRange = new CourseRange();

        //通过年级获取课程类别
        List<TeachingClass> teachingClasses = teachingClassMapper.selectList(new LambdaQueryWrapper<TeachingClass>().eq(TeachingClass::getTaskId, taskId)
                .in(TeachingClass::getGradeId, gradeIds).eq(TeachingClass::getTeachingClassType, TeachingClassType.SELECT.getCode())
                .eq(TeachingClass::getDeleted, Deleted.NOT.getCode()));

        List<Long> selectCourseIds = teachingClasses.stream().map(TeachingClass::getCourseId).distinct().collect(Collectors.toList());


        List<Course> courseList = courseMapper.selectList(new LambdaQueryWrapper<Course>()
                .eq(Course::getTaskId, taskId).in(Course::getCourseId, selectCourseIds)
                .eq(Course::getDeleted, Deleted.NOT.getCode()));

        List<String> courseTypeId = courseList.stream().map(Course::getCourseTypeId).collect(Collectors.toList());
        List<String> courseTypeName = courseList.stream().map(Course::getCourseType).collect(Collectors.toList());

        if (CollUtil.isNotEmpty(courseTypeId) && CollUtil.isNotEmpty(courseTypeName)) {
            courseRange.setCourseTypeId(courseTypeId);
            courseRange.setCourseTypeName(courseTypeName);
        }

        return courseRange;


    }

    @Override
    public CssTaskRound addOrUpdateCssTaskRoundWX(CssTaskRound cssTaskRound) {
        cssTaskRoundService.saveOrUpdate(cssTaskRound);
        //获取选课任务
        CssTaskInfo cssTaskInfo = this.getById(cssTaskRound.getCssTaskId());

        //发送消息
        String userId = request.getHeader("userId");
        String userName = request.getHeader("userName");
        String authorization = request.getHeader("Authorization");


        String module = "";
        if (cssTaskRound.getSelectRound() == 1) {
            module = "正选";
        } else if (cssTaskRound.getSelectRound() == 2) {
            module = "补选";
        } else if (cssTaskRound.getSelectRound() == 3) {
            module = "退选";
        }

        if (StrUtil.isNotBlank(module) && ObjUtil.isNotNull(cssTaskRound.getBeginTime()) && ObjUtil.isNotNull(cssTaskRound.getEndTime())) {
            yunService.sendSelectCourseStartMsgWX(cssTaskRound.getBeginTime(), cssTaskRound.getEndTime(), cssTaskInfo.getTaskId(), module, userId, userName, authorization);
        }
        redisUtil.del(GlobalConstant.REDIS_ROUND_KEY);

        return cssTaskRound;
    }


}